home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK1.toast / Development Kits (Disc 1) / QuickDraw 3D / Samples / SampleCode / Plug-in - WireFrame Renderer / SR_PipelineSetup.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-14  |  20.0 KB  |  745 lines  |  [TEXT/MPS ]

  1. /******************************************************************************
  2.  **                                                                             **
  3.  **     Module:        SR_PipelineSetup.c                                         **
  4.  **                                                                          **
  5.  **                                                                          **
  6.  **     Purpose:     Sample Renderer setup routines, etc.                      **
  7.  **                                                                          **
  8.  **                                                                          **
  9.  **                                                                          **
  10.  **     Copyright (C) 1996 Apple Computer, Inc.  All rights reserved.          **
  11.  **                                                                          **
  12.  **                                                                             **
  13.  *****************************************************************************/
  14. #include <assert.h>
  15.  
  16. #include "QD3D.h"
  17. #include "QD3DMath.h"
  18. #include "QD3DCamera.h"
  19. #include "QD3DView.h"
  20. #include "QD3DTransform.h"
  21. #include "QD3DRenderer.h"
  22. #include "QD3DShader.h"
  23.  
  24. #include "SR.h"
  25. #include "SR_Math.h"
  26.  
  27. /******************************************************************************
  28.  **                                                                             **
  29.  **                                Setup Routines                                 **
  30.  **                                                                             **
  31.  *****************************************************************************/
  32.  
  33. /*===========================================================================*\
  34.  *
  35.  *    Routine:    SR_ClipPlanesInDC()
  36.  *
  37.  *    Comments:    Compute clipping planes in device coordinates
  38.  *
  39. \*===========================================================================*/
  40.  
  41. static void SR_ClipPlanesInDC(
  42.     TQ3Matrix4x4     *frustumToDC, 
  43.     float             *clipPlanesInDC)
  44. {
  45.     TQ3Point3D            clipPlanesInFrustum[2];
  46.     TQ3RationalPoint4D    clipPlanesInDevCoords[2];
  47.     
  48.     assert(frustumToDC        != NULL);
  49.     assert(clipPlanesInDC    != NULL);
  50.  
  51.     clipPlanesInFrustum[0].x = -1.0;    /* xmin */
  52.     clipPlanesInFrustum[0].y = -1.0;    /* ymin */
  53.     clipPlanesInFrustum[0].z = -1.0;    /* zmin */
  54.     
  55.     clipPlanesInFrustum[1].x =  1.0;    /* xmax */
  56.     clipPlanesInFrustum[1].y =  1.0;    /* ymax */
  57.     clipPlanesInFrustum[1].z =  0.0;    /* zmax */
  58.  
  59.     Q3Point3D_To4DTransformArray(
  60.         (TQ3Point3D *)&clipPlanesInFrustum[0],
  61.         frustumToDC, 
  62.         &clipPlanesInDevCoords[0],
  63.         2, 
  64.         sizeof(TQ3Point3D), 
  65.         sizeof(TQ3RationalPoint4D));
  66.  
  67.     /*
  68.      *  xmin, xmax
  69.      */
  70.     clipPlanesInDC[0] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[0].x);
  71.     clipPlanesInDC[1] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[1].x - 1.0);
  72.  
  73.     /*
  74.      *  ymin, ymax
  75.      */
  76.     clipPlanesInDC[2] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[1].y);
  77.     clipPlanesInDC[3] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[0].y - 1.0);
  78.  
  79.     /*
  80.      *  zmin, zmax
  81.      */
  82.     clipPlanesInDC[4] = clipPlanesInDevCoords[0].z;
  83.     clipPlanesInDC[5] = clipPlanesInDevCoords[1].z;
  84. }
  85.  
  86.  
  87. /******************************************************************************
  88.  **                                                                             **
  89.  **                                Attributes Update                             **
  90.  **                                                                             **
  91.  *****************************************************************************/
  92.  
  93. /*===========================================================================*\
  94.  *
  95.  *    Routine:    SR_Update_DiffuseColor()
  96.  *
  97.  *    Comments:    Tracks changes to diffuse color state in the view
  98.  *
  99. \*===========================================================================*/
  100.  
  101. TQ3Status SR_Update_DiffuseColor(
  102.     TQ3ViewObject             view,
  103.     TSRPrivate                *srPrivate,
  104.     TQ3ColorRGB                *diffuseColor)
  105. {
  106.     UNUSED(view);
  107.     
  108.     assert(view         != NULL);
  109.     assert(srPrivate     != NULL);
  110.     assert(diffuseColor != NULL);
  111.  
  112.     srPrivate->viewDiffuseColor = *diffuseColor;
  113.     
  114.     return (kQ3Success);
  115. }
  116.  
  117.  
  118. /*===========================================================================*\
  119.  *
  120.  *    Routine:    SR_Update_HighlightState()
  121.  *
  122.  *    Comments:    Tracks changes to highlight state in the view
  123.  *
  124. \*===========================================================================*/
  125.  
  126. TQ3Status SR_Update_HighlightState(
  127.     TQ3ViewObject             view,
  128.     TSRPrivate                *srPrivate,
  129.     TQ3Boolean                *highlightState)
  130. {
  131.     UNUSED(view);
  132.     
  133.     assert(view         != NULL);
  134.     assert(srPrivate     != NULL);
  135.  
  136.     srPrivate->viewHighlightState = 
  137.         (highlightState == NULL) ? kQ3False : *highlightState;
  138.  
  139.     return (kQ3Success);
  140. }
  141.  
  142.  
  143. /******************************************************************************
  144.  **                                                                             **
  145.  **                                    Styles                                     **
  146.  **                                                                             **
  147.  *****************************************************************************/
  148.  
  149. /*===========================================================================*\
  150.  *
  151.  *    Routine:    SR_Update_BackfacingStyle()
  152.  *
  153.  *    Comments:    Tracks changes to backfacing style state in the view
  154.  *
  155. \*===========================================================================*/
  156.  
  157. TQ3Status SR_Update_BackfacingStyle(
  158.     TQ3ViewObject             view,
  159.     TSRPrivate                *srPrivate,
  160.     TQ3BackfacingStyle        *backfacingStyle)
  161. {
  162.     UNUSED(view);
  163.     
  164.     assert(view             != NULL);
  165.     assert(srPrivate         != NULL);
  166.     assert(backfacingStyle     != NULL);
  167.         
  168.     srPrivate->backfacingStyle = *backfacingStyle;    
  169.     
  170.     return (kQ3Success);
  171. }
  172.  
  173.  
  174. /*===========================================================================*\
  175.  *
  176.  *    Routine:    SR_Update_OrientationStyle()
  177.  *
  178.  *    Comments:    Tracks changes to orientation style state in the view
  179.  *
  180. \*===========================================================================*/
  181.  
  182. TQ3Status SR_Update_OrientationStyle(
  183.     TQ3ViewObject             view,
  184.     TSRPrivate                *srPrivate,
  185.     TQ3OrientationStyle        *orientationStyle)
  186. {
  187.     UNUSED(view);
  188.     
  189.     assert(view             != NULL);
  190.     assert(srPrivate        != NULL);
  191.     assert(orientationStyle != NULL);
  192.         
  193.     srPrivate->orientationStyle = *orientationStyle;
  194.     
  195.     return (kQ3Success);
  196. }
  197.  
  198.  
  199. /*===========================================================================*\
  200.  *
  201.  *    Routine:    SR_Update_FillStyle()
  202.  *
  203.  *    Comments:    Tracks changes to fill style state in the view
  204.  *
  205. \*===========================================================================*/
  206.  
  207. TQ3Status SR_Update_FillStyle(
  208.     TQ3ViewObject             view,
  209.     TSRPrivate                *srPrivate,
  210.     TQ3FillStyle            *fillStyle)
  211. {
  212.     UNUSED(view);
  213.     
  214.     assert(view         != NULL);
  215.     assert(srPrivate     != NULL);
  216.     assert(fillStyle     != NULL);
  217.     
  218.     srPrivate->viewFillStyle = *fillStyle;
  219.     
  220.     return (kQ3Success);
  221. }
  222.  
  223.  
  224. /*===========================================================================*\
  225.  *
  226.  *    Routine:    SR_Update_HighlightStyle()
  227.  *
  228.  *    Comments:    Tracks changes to highlight style state in the view
  229.  *
  230. \*===========================================================================*/
  231.  
  232. TQ3Status SR_Update_HighlightStyle(
  233.     TQ3ViewObject             view,
  234.     TSRPrivate                *srPrivate,
  235.     TQ3AttributeSet            *highlightAttributeSet)
  236. {
  237.     UNUSED(view);
  238.     
  239.     assert(view         != NULL);
  240.     assert(srPrivate     != NULL);
  241.  
  242.     /*
  243.      *  Bump reference count on incoming attribute set, if any 
  244.      */
  245.     if (*highlightAttributeSet != NULL) {
  246.         Q3Shared_GetReference(*highlightAttributeSet);
  247.     }
  248.     
  249.     /*
  250.      *  Get rid of existing highlight attribute set
  251.      */
  252.     if (srPrivate->viewHighlightAttributeSet != NULL) {
  253.         Q3Object_Dispose(srPrivate->viewHighlightAttributeSet);
  254.     }
  255.     
  256.     /*
  257.      *  Set the highlight attribute set
  258.      */
  259.     srPrivate->viewHighlightAttributeSet = *highlightAttributeSet;
  260.         
  261.     return (kQ3Success);
  262. }
  263.  
  264.  
  265. /*===========================================================================*\
  266.  *
  267.  *    Routine:    SR_UpdateNonInvertibleMatrix()
  268.  *
  269.  *    Comments:    Deal with noninvertible local-to-world matrices
  270.  *
  271. \*===========================================================================*/
  272.  
  273. static void SR_UpdateNonInvertibleMatrix(
  274.     TSRPrivate            *srPrivate,
  275.     TQ3Boolean            parallelProjection)
  276. {
  277.     TQ3Matrix3x3    submatrix;
  278.     long             indexPtr[3];
  279.     float             rowInterchanges;
  280.     long            rank;
  281.  
  282.     /* Singular */
  283.     
  284.     /* Copy upper-left 3x3 submatrix to temporary array */
  285.     submatrix.value[0][0]    = srPrivate->transforms.localToWorld.value[0][0];
  286.     submatrix.value[0][1]    = srPrivate->transforms.localToWorld.value[0][1];
  287.     submatrix.value[0][2]    = srPrivate->transforms.localToWorld.value[0][2];
  288.     submatrix.value[1][0]    = srPrivate->transforms.localToWorld.value[1][0];
  289.     submatrix.value[1][1]    = srPrivate->transforms.localToWorld.value[1][1];
  290.     submatrix.value[1][2]    = srPrivate->transforms.localToWorld.value[1][2];
  291.     submatrix.value[2][0]    = srPrivate->transforms.localToWorld.value[2][0];
  292.     submatrix.value[2][1]    = srPrivate->transforms.localToWorld.value[2][1];
  293.     submatrix.value[2][2]    = srPrivate->transforms.localToWorld.value[2][2];
  294.  
  295.     /* 
  296.      *  Decompose singular matrix into lower-triangular/upper-echelon form 
  297.      */
  298.     SRMatrix_LUDecomposeSingular3x3(
  299.         submatrix.value,
  300.         indexPtr, 
  301.         &rowInterchanges,
  302.         &rank);
  303.  
  304.     srPrivate->normalLocalToWorldRank = rank;
  305.  
  306.     if ((rank == 2) &&
  307.         (SRMatrix_ComputeFlatLand(
  308.             &srPrivate->transforms.localToWorld,
  309.             &submatrix,
  310.             parallelProjection,
  311.             &srPrivate->eyeVectorInWorldCoords,
  312.             &srPrivate->eyePointInWorldCoords,
  313.             &srPrivate->eyeVectorInLocalCoords,
  314.             &srPrivate->flatWorld) == kQ3Success)) {
  315.         return;
  316.     }
  317.  
  318.     /*
  319.      *  Fill in some arbitrary values if failure, or rank is 0, 1, or 3
  320.      *
  321.      *  rank = 0
  322.      *
  323.      *  All geometry is transformed to a single point.  Lighting and 
  324.      *  culling have no meaning so choose any arbitrary eye vector.
  325.      *  Renderers should render nothing, but let's set the values
  326.      *  to something in case they try to use them.
  327.      *
  328.      *  rank = 0
  329.      *
  330.      *  All geometry is transformed to a line.   Lighting and
  331.      *  culling have no meaning so choose any arbitrary eye vector.
  332.      *  Renderers should render nothing, but let's set the values
  333.      *  to something in case they try to use them.
  334.      *
  335.      *  rank = 1
  336.      *
  337.      *  TODO:
  338.      *  The 4x4 matrix is singular, but the 3x3 upper-left submatrix
  339.      *  has rank 3 so the element[3][3] is zero.  This means that
  340.      *  all geometry is transformed to infinity.  This is useful concept
  341.      *  for mathematicians, but for now, let's do the wrong thing until
  342.      *  somebody reports a bug.
  343.      */
  344.  
  345.     SRVector4D_Set(&srPrivate->eyeVectorInLocalCoords, 0.0, 0.0, 1.0, 0.0);
  346.     Q3Vector3D_Set(&srPrivate->flatWorld.normal, 0.0, 0.0, 1.0);
  347.     srPrivate->flatWorld.constant = 0.0;
  348. }
  349.  
  350.  
  351. /*===========================================================================*\
  352.  *
  353.  *    Routine:    SR_Update_LocalToWorldMatrix()
  354.  *
  355.  *    Comments:    Track changes to local-to-world transform
  356.  *
  357. \*===========================================================================*/
  358.  
  359. TQ3Status SR_Update_LocalToWorldMatrix(
  360.     TQ3ViewObject             view,
  361.     TSRPrivate                *srPrivate,
  362.     TQ3Matrix4x4            *localToWorld)
  363. {
  364.     assert(view != NULL);
  365.     assert(srPrivate != NULL);
  366.     assert(localToWorld != NULL);
  367.     
  368.     UNUSED(view);
  369.     
  370.     srPrivate->transforms.localToWorld = *localToWorld;
  371.     
  372.     srPrivate->transforms.dirtyLocalToWorld = kQ3True;
  373.     srPrivate->transforms.validLocalToWorld = kQ3True;
  374.     
  375.     return (kQ3Success);
  376. }
  377.  
  378.  
  379. /*===========================================================================*\
  380.  *
  381.  *    Routine:    SR_Update_LocalToFrustumMatrix()
  382.  *
  383.  *    Comments:    Track changes to local-to-frustum transform
  384.  *
  385. \*===========================================================================*/
  386.  
  387. TQ3Status SR_Update_LocalToFrustumMatrix(
  388.     TQ3ViewObject             view,
  389.     TSRPrivate                *srPrivate,
  390.     TQ3Matrix4x4            *localToFrustum)
  391. {
  392.     assert(view             != NULL);
  393.     assert(srPrivate         != NULL);
  394.     assert(localToFrustum     != NULL);
  395.     
  396.     UNUSED(view);
  397.     
  398.     srPrivate->transforms.localToFrustum = *localToFrustum;
  399.     
  400.     srPrivate->transforms.dirtyLocalToFrustum = kQ3True;
  401.     srPrivate->transforms.validLocalToFrustum = kQ3True;
  402.     
  403.     return (kQ3Success);
  404. }
  405.  
  406.  
  407. /*===========================================================================*\
  408.  *
  409.  *    Routine:    SR_Update_WorldToFrustumMatrix()
  410.  *
  411.  *    Comments:    Track changes to world-to-frustum transform
  412.  *
  413. \*===========================================================================*/
  414.  
  415. TQ3Status SR_Update_WorldToFrustumMatrix(
  416.     TQ3ViewObject             view,
  417.     TSRPrivate                *srPrivate,
  418.     TQ3Matrix4x4            *worldToFrustum)
  419. {
  420.     assert(view             != NULL);
  421.     assert(srPrivate         != NULL);
  422.     assert(worldToFrustum     != NULL);
  423.     
  424.     UNUSED(view);
  425.     
  426.     srPrivate->transforms.worldToFrustum = *worldToFrustum;
  427.     
  428.     srPrivate->transforms.dirtyWorldToFrustum = kQ3True;
  429.     srPrivate->transforms.validWorldToFrustum = kQ3True;
  430.     
  431.     return (kQ3Success);
  432. }
  433.  
  434.  
  435. /*===========================================================================*\
  436.  *
  437.  *    Routine:    SR_SetupRegionDependentTransformations()
  438.  *
  439.  *    Comments:    Update the frustum-to-device coordinates transform
  440.  *
  441. \*===========================================================================*/
  442.  
  443. TQ3Status SR_SetupRegionDependentTransformations(
  444.     TSRPrivate     *srPrivate)
  445. {
  446.     float        frustumWindow[6] = { -1.0, 1.0, 1.0, -1.0, -1.0, 0.0 };
  447.     float        deviceViewport[6];
  448.     TQ3Status    status = kQ3Success;
  449.     
  450.     /*
  451.      *  The orientation of Frustum Coordinates is x right, y up, z toward.
  452.      *  The orientation of Device Coordinates is x right, y down, z toward.
  453.      *  The y axis is inverted in the mapping so the min and max values are
  454.      *  reversed in the Frustum boundaries.
  455.      */
  456.     {    
  457.         float    tmp1, tmp2;
  458.         
  459.         /*
  460.          *  xmin
  461.          */
  462.         Q3XDrawRegion_GetDeviceOffsetX(srPrivate->drawRegion, &tmp1);
  463.         deviceViewport[0] =  tmp1;
  464.         
  465.         /*
  466.          *  xmax
  467.          */
  468.         Q3XDrawRegion_GetWindowScaleX(srPrivate->drawRegion, &tmp1);
  469.         Q3XDrawRegion_GetDeviceOffsetX(srPrivate->drawRegion, &tmp2);
  470.         deviceViewport[1] =  tmp1 - 1.0 + tmp2;
  471.         
  472.         /*
  473.          *  ymin
  474.          */         
  475.         Q3XDrawRegion_GetDeviceOffsetY(srPrivate->drawRegion, &tmp1);         
  476.         deviceViewport[2] =  tmp1;
  477.         
  478.         /*
  479.          *  ymax
  480.          */
  481.         Q3XDrawRegion_GetWindowScaleY(srPrivate->drawRegion, &tmp1);
  482.         Q3XDrawRegion_GetDeviceOffsetY(srPrivate->drawRegion, &tmp2);
  483.         deviceViewport[3] =  tmp1 - 1.0  + tmp2;
  484.                              
  485.         /*
  486.          *  zmin, xmax
  487.          */                 
  488.         deviceViewport[4] = -1.0;
  489.         deviceViewport[5] =  0.0;
  490.     }
  491.     
  492.     srPrivate->transforms.frustumToDC.value[0][0] 
  493.         = (deviceViewport[1] - deviceViewport[0]) / 
  494.                 (frustumWindow[1] - frustumWindow[0]);
  495.     srPrivate->transforms.frustumToDC.value[1][1] 
  496.         = (deviceViewport[3] - deviceViewport[2]) / 
  497.                 (frustumWindow[3] - frustumWindow[2]);
  498.     srPrivate->transforms.frustumToDC.value[2][2] 
  499.         = (deviceViewport[5] - deviceViewport[4]) / 
  500.                 (frustumWindow[5] - frustumWindow[4]);
  501.     srPrivate->transforms.frustumToDC.value[3][0] 
  502.         = - srPrivate->transforms.frustumToDC.value[0][0] * 
  503.                 frustumWindow[0] + deviceViewport[0];
  504.     srPrivate->transforms.frustumToDC.value[3][1] 
  505.         = - srPrivate->transforms.frustumToDC.value[1][1] * 
  506.                 frustumWindow[2] + deviceViewport[2];
  507.     srPrivate->transforms.frustumToDC.value[3][2] 
  508.         = - srPrivate->transforms.frustumToDC.value[2][2] * 
  509.                 frustumWindow[4] + deviceViewport[4];
  510.     
  511.     {
  512.         TQ3Matrix4x4    *deviceTransform;
  513.  
  514.         if (Q3XDrawRegion_GetDeviceTransform(
  515.                 srPrivate->drawRegion, 
  516.                 &deviceTransform) != NULL) {
  517.             SR_ClipPlanesInDC(
  518.                 deviceTransform, 
  519.                 &srPrivate->clipPlanesInDC[0]);
  520.         } else {
  521.             status = kQ3Failure;
  522.         }
  523.     }
  524.     
  525.     srPrivate->transforms.dirtyFrustumToDC = kQ3False;
  526.     srPrivate->transforms.validFrustumToDC = kQ3True;
  527.     
  528.     return (status);
  529. }
  530.  
  531.  
  532. /*===========================================================================*\
  533.  *
  534.  *    Routine:    SR_SetupPipelineInitCamera()
  535.  *
  536.  *    Comments:    Set up intitial camera info.
  537.  *
  538. \*===========================================================================*/
  539.  
  540. void SR_SetupPipelineInitCamera(
  541.     TSRPrivate            *srPrivate,
  542.     TQ3CameraObject        camera)
  543. {
  544.     assert(srPrivate != NULL);
  545.     assert(camera != NULL);
  546.     
  547.     if (srPrivate->camera != NULL) {
  548.         Q3Object_Dispose(srPrivate->camera);
  549.     }
  550.     
  551.     srPrivate->camera        = Q3Shared_GetReference(camera);
  552.     srPrivate->cameraType    = Q3Camera_GetType(camera);
  553.     Q3Camera_GetPlacement(camera, &srPrivate->cameraPlacement);
  554.     
  555.     srPrivate->backfacingStyle     = kQ3BackfacingStyleRemove;
  556.     srPrivate->orientationStyle    = kQ3OrientationStyleCounterClockwise;
  557.  
  558.     Q3Matrix4x4_SetIdentity(&srPrivate->transforms.localToWorld);
  559.     srPrivate->transforms.dirtyLocalToWorld = kQ3False;
  560.     srPrivate->transforms.validLocalToWorld = kQ3False;
  561.     
  562.     Q3Matrix4x4_SetIdentity(&srPrivate->transforms.worldToFrustum);
  563.     srPrivate->transforms.dirtyWorldToFrustum = kQ3False;
  564.     srPrivate->transforms.validWorldToFrustum = kQ3False;
  565.     
  566.     Q3Matrix4x4_SetIdentity(&srPrivate->transforms.localToFrustum);
  567.     srPrivate->transforms.dirtyLocalToFrustum = kQ3False;
  568.     srPrivate->transforms.validLocalToFrustum = kQ3False;
  569.     
  570.     Q3Matrix4x4_SetIdentity(&srPrivate->transforms.frustumToDC);
  571.     srPrivate->transforms.dirtyFrustumToDC = kQ3False;
  572.     srPrivate->transforms.validFrustumToDC = kQ3False;
  573.     
  574.     Q3Matrix4x4_SetIdentity(&srPrivate->transforms.localToDC);
  575.     srPrivate->transforms.dirtyLocalToDC = kQ3False;
  576.     srPrivate->transforms.validLocalToDC = kQ3False;
  577. }
  578.  
  579.  
  580. /*===========================================================================*\
  581.  *
  582.  *    Routine:    SR_SetupPipelineExit()
  583.  *
  584.  *    Comments:    Dispose of things to which SR has a reference.
  585.  *
  586. \*===========================================================================*/
  587.  
  588. void SR_SetupPipelineExit(
  589.     TSRPrivate            *srPrivate)
  590. {
  591.     assert(srPrivate != NULL);
  592.     
  593.     /*
  594.      *  Dispose of highlight attribute set
  595.      */
  596.     if (srPrivate->viewHighlightAttributeSet != NULL) {
  597.         Q3Object_Dispose(srPrivate->viewHighlightAttributeSet);
  598.         srPrivate->viewHighlightAttributeSet = NULL;
  599.     }
  600.     
  601.     /*
  602.      *  Dispose of camera
  603.      */
  604.     if (srPrivate->camera != NULL) {
  605.         Q3Object_Dispose(srPrivate->camera);
  606.         srPrivate->camera = NULL;
  607.     }
  608. }
  609.  
  610.  
  611. /*===========================================================================*\
  612.  *
  613.  *    Routine:    SR_UpdatePipeline()
  614.  *
  615.  *    Comments:    
  616.  *
  617. \*===========================================================================*/
  618.  
  619. TQ3Status SR_UpdatePipeline(
  620.     TSRPrivate            *srPrivate)
  621. {
  622.     TQ3Status            status = kQ3Success;
  623.  
  624.     assert(srPrivate != NULL);
  625.     
  626.     /*
  627.      *  If no transforms have changed since the last primitive,
  628.      *  then simply return.
  629.      */
  630.     if (!(srPrivate->transforms.dirtyLocalToWorld        ||
  631.           srPrivate->transforms.dirtyLocalToFrustum        ||
  632.           srPrivate->transforms.dirtyWorldToFrustum        ||
  633.           srPrivate->transforms.dirtyFrustumToDC        ||
  634.           srPrivate->transforms.dirtyLocalToDC)) {
  635.         return (kQ3Success);
  636.     }
  637.             
  638.     /*
  639.      *  Create local-to-device-coordinates transform
  640.      */
  641.     Q3Matrix4x4_Multiply(
  642.         &srPrivate->transforms.localToFrustum, 
  643.         &srPrivate->transforms.frustumToDC, 
  644.         &srPrivate->transforms.localToDC);
  645.         
  646.     /*
  647.      *
  648.      *    Compute eye vector or point in local coordinates by
  649.      *    transforming the eye vector or point in world coordinates
  650.      *    back to local coordinates.  If the local-to-world matrix
  651.      *    is singular and the upper-left 3x3 submatrix has rank 2, 
  652.      *    compute the eye vector in local coordinates and the plane
  653.      *    equation of the transformed geometry in world coordinates
  654.      *    such that the vectors in both coordinate systems points
  655.      *    in the same hemisphere as the eye.
  656.      *
  657.      */
  658.     if (srPrivate->cameraType == kQ3CameraTypeOrthographic)  {    
  659.         TQ3Vector3D        v;
  660.         TSRVector4D        eyeVectorWC;
  661.         TQ3Matrix4x4    inverse;
  662.         
  663.         /* Orthographic projection */
  664.  
  665.         /* Calculate world eye vector from camera placement */
  666.         Q3Point3D_Subtract(
  667.             &srPrivate->cameraPlacement.cameraLocation,
  668.             &srPrivate->cameraPlacement.pointOfInterest,
  669.             &v);
  670.         SRVector3D_To4D(&v, &eyeVectorWC);
  671.         SRVector4D_Normalize(&eyeVectorWC, &srPrivate->eyeVectorInWorldCoords);
  672.         
  673.         if (Q3Matrix4x4_Invert(
  674.                 &srPrivate->transforms.localToWorld, 
  675.                 &inverse) != NULL)  {
  676.             TSRVector4D        eyeVectorLC;
  677.             TQ3Matrix4x4    transpose;
  678.             
  679.             /* Nonsingular */
  680.             Q3Matrix4x4_Transpose(
  681.                 &srPrivate->transforms.localToWorld,
  682.                 &transpose);
  683.             SRVector4D_Transform(
  684.                 &eyeVectorWC,
  685.                 &transpose,
  686.                 &eyeVectorLC);
  687.             SRVector4D_Normalize(
  688.                 &eyeVectorLC,
  689.                 &srPrivate->eyeVectorInLocalCoords);
  690.             
  691.             srPrivate->normalLocalToWorldRank = 3;
  692.         } else {
  693.             SR_UpdateNonInvertibleMatrix(srPrivate, kQ3True);
  694.         }
  695.     } else    /* Non orthographic */ {
  696.         TQ3Matrix4x4        inverse;
  697.         
  698.         /* Perspective projection */
  699.         
  700.         /* Get world eye location from camera placement */
  701.         Q3Point3D_To4D(
  702.             &srPrivate->cameraPlacement.cameraLocation,
  703.             &srPrivate->eyePointInWorldCoords);
  704.         
  705.         if (Q3Matrix4x4_Invert(
  706.                 &srPrivate->transforms.localToWorld,
  707.                 &inverse) != NULL) {
  708.             float                wValue;
  709.  
  710.             /* Nonsingular */
  711.             Q3RationalPoint4D_Transform(
  712.                 &srPrivate->eyePointInWorldCoords, 
  713.                 &inverse, 
  714.                 &srPrivate->eyePointInLocalCoords);
  715.     
  716.             wValue = 1.0 / srPrivate->eyePointInLocalCoords.w;
  717.             
  718.             srPrivate->eyePointInLocalCoords.x *= wValue;
  719.             srPrivate->eyePointInLocalCoords.y *= wValue;
  720.             srPrivate->eyePointInLocalCoords.z *= wValue;
  721.             srPrivate->eyePointInLocalCoords.w  = 1.0;
  722.             
  723.             srPrivate->normalLocalToWorldRank = 3;
  724.         } else {
  725.             SR_UpdateNonInvertibleMatrix(srPrivate, kQ3False);
  726.         }
  727.     }
  728.     
  729.     /*
  730.      *  Set the states of the transforms to "clean"
  731.      */
  732.     srPrivate->transforms.dirtyLocalToWorld     = kQ3False;
  733.     srPrivate->transforms.dirtyLocalToFrustum     = kQ3False;
  734.     srPrivate->transforms.dirtyWorldToFrustum    = kQ3False;
  735.     srPrivate->transforms.dirtyFrustumToDC         = kQ3False;
  736.     srPrivate->transforms.dirtyLocalToDC         = kQ3False;
  737.     
  738.     /*
  739.      *  Set validity state
  740.      */
  741.     srPrivate->transforms.validLocalToDC        = kQ3True;
  742.     
  743.     return (kQ3Success);
  744. }
  745.